Computer programs are typically written in either a high level language, such as C.sup.++, or in assembly language. In the process of producing an executable program, a high level language program is first compiled and then linked to form a binary executable program. (Alternatively, a high level language may be interpreted to produce a binary executable program.) When the executable program runs, it may load DLL files, which it calls. All of the modules, whether dynamically- or statically-loaded, execute within an environment that defines many of the characteristics that are "apparent" to the executing program. These characteristics include, for example, the directory structure in which the program executes, the names of the files it expects to see, the names of the modules that are loaded by the program, and the name of the executing program itself.
The need frequently arises to examine (through software means) a binary executable. The examination may be necessary for reasons of analysis, performance evaluation, security, error checking, or to understand the structure of the executable program, and possibly, to modify that structure to produce another binary executable related to the original binary executable. The modified binary executable will preferably perform the same function as the original, but in addition, may measure some characteristic of the program's behavior as it runs, restrict that program's behavior in some way, or verify its operation as it executes. In order to perform this function correctly, it must be possible to modify any (and perhaps all) of the executable components that comprise the binary executable, producing new executable components that have the same original function, plus the intended modified or additional functions, while maintaining the original execution environment. By at least simulating the original execution environment, the transformed program and its modules will be unaware that anything has changed in the environment. Meeting both of these goals simultaneously is difficult, because in satisfying the first goal, some characteristics of the environment must be changed. In particular, it is often necessary to create new versions of the executable modules that may need to be stored in new file locations, and under new names. Also, one or more new executable modules may be needed. Such changes must be addressed in creating a transformed program that seems to run as if it were the original program.
The following simple example illustrates the importance of preserving the original program's environment. Suppose an original program named "prog.exe" contains the following code intended to open a data file expected to be named "prog.data."
# Invoke a system-level routine to determine the name of the
# current program (this instruction should return "prog.exe")
progName=GetProgramName();
# Replacing the suffix ".exe" with the suffix ".data"
# (in the following instruction should return "Prog.data")
datafileName=ReplaceSuffix(progName, ".exe", ".data");
# Open the data file.
datafile=OpenFile(datafileName)
Now suppose that a binary-rewriting tool creates a new instrumented version of "prog.exe," called "prog-new.exe." Unless the environment is preserved, the transformed program "prog-new.exe" will fail to find the data file, because it will try to open "prog-new.data" rather than the original data file, "prog.data."
An apparent solution to this particular problem would be to give the transformed program the same name as the original program, and similarly, to give transformed DLLs the same names as the original DLLs. However, this solution is unsatisfactory, because it effectively changes the environment of the entire system. Specifically, references to the original program or DLLs that the original program accesses may accidentally refer to the transformed versions, resulting in unexpected behavior. Such unintended references to the transformed versions could come from users, other (non-transformed) programs, or even from code added to the program during the transformation process.
Accordingly, it will be evident that a solution to this problem must be found that avoids these problems. The solution should enable executable programs that have been transformed to run correctly without regard to any changes in their environment. Further, the task of creating a transformed program that meets this requirement should preferably be carried out using a program modifying tool that is implemented on a computer, requiring only minimal interactive input from a user.